package operations;

import gui.Notification;
import gui.Window;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Properties;

import com.bbn.openmap.omGraphics.OMPoint;
import com.bbn.openmap.omGraphics.OMPoly;
import com.bbn.openmap.util.SwingWorker;

import components.Arc;
import components.ArcList;
import components.Coordinate;
import components.Node;
import components.NodeList;

public class SW{
	
	private int operations;
	
	private ArrayList<Lifo> lifo;
	
	private SwingWorker worker;
	
	private File nodeFile;
	private File arcFile;
	
	private Window window;
	
	public SW(Window window, Properties props){
		lifo = new ArrayList<Lifo>();
		
		try{
			nodeFile = new File(props.getProperty("nodes.file"));
			arcFile = new File(props.getProperty("arcs.file"));
			if(nodeFile == null || arcFile == null){
				throw new IOException();
			}
		}
		catch(IOException e){
			Notification.createErrorDialog("Bledne dane", "Nie mozna zaladowac plikow zrodlowych");
			System.exit(0);
		}
		
		worker = new SwingWorker() {
			
			public Object construct() {
				return doWork();
			}

		};
	}
	
	public void addNewEvent(Lifo event){
		lifo.add(event);
	}
	
	private Object doWork() {
		while(true){
			try{
				if(lifo.size() == 0){
					if(Thread.interrupted()){
						throw new InterruptedException();
					}
					Thread.sleep(1000);
				}
				else{
					if(Lifo.addNode.equals(lifo.get(0).getEvent())){
						addNode(lifo.get(0).getObj());
						lifo.remove(0);
					}
					else if(Lifo.editNode.equals(lifo.get(0).getEvent())){
						editNode(lifo.get(0).getObj());
						lifo.remove(0);
					}
					else if(Lifo.delNode.equals(lifo.get(0).getEvent())){
						delNode(lifo.get(0).getObj());
						lifo.remove(0);
					}
					else if(Lifo.addArc.equals(lifo.get(0).getEvent())){
						addArc(lifo.get(0).getObj());
						lifo.remove(0);
					}
					else if(Lifo.editArc.equals(lifo.get(0).getEvent())){
						editArc(lifo.get(0).getObj());
						lifo.remove(0);
					}
					else if(Lifo.delArc.equals(lifo.get(0).getEvent())){
						delArc(lifo.get(0).getObj());
						lifo.remove(0);
					}
				}
			}catch(InterruptedException e){
				break;
			}
		}
		return "end";
	}

	private void delArc(Object object) {
		int pos = this.window.getArcList().findGraphicPosition((OMPoly) object);
		if(pos >= 0){
			int[] line = new int[3];
			line[0] = this.window.getArcList().getArcs().get(pos).getLine();
			line[1] = this.window.getArcList().getArcs().get(pos).getStartNode().getLine();
			line[1] = this.window.getArcList().getArcs().get(pos).getEndNode().getLine();
			this.window.getArcList().getArcs().get(pos).getStartNode().decrementArcs();
			this.window.getArcList().getArcs().get(pos).getEndNode().decrementArcs();
			
			this.window.getArcList().changeLine(pos);
			
			this.window.getArcList().getArcs().remove(pos);
			
			this.saveArcs(true,line[0],true);
			this.saveNodes(false,line[1],false);
			this.saveNodes(false, line[0],false);
		}
	}

	private void editArc(Object object) {
		if(object instanceof OMPoly){
			int pos = this.window.getArcLayer().findGraphicPosition((OMPoly) object);
			if(pos >= 0){
				Arc arc = this.window.getArcList().getArcs().get(pos);
				float[] cords = ((OMPoly) object).getLatLonArray();
				if(this.window.getNodeList().findGraphicPosition(new OMPoint(cords[0], cords[1])) < 0){
					this.window.getNodeLayer().addNode(new Node(
							this.window.getNodeList().getNodes().get(this.window.getNodeList().getNodes().size() -1).getId() + 1,
							cords[1],
							cords[0],
							1,
							this.window.getNodeList().getNodes().get(this.window.getNodeList().getNodes().size() -1).getLine() + 1
							));
				}
				arc.getStartNode().setLattitude(cords[0]);
				arc.getStartNode().setLongtitude(cords[1]);
				if(this.window.getNodeList().findGraphicPosition(new OMPoint(cords[cords.length-2], cords[cords.length-1])) < 0){
					this.window.getNodeLayer().addNode(new Node(
							this.window.getNodeList().getNodes().get(this.window.getNodeList().getNodes().size() -1).getId() + 1,
							cords[cords.length-1],
							cords[cords.length-2],
							1,
							this.window.getNodeList().getNodes().get(this.window.getNodeList().getNodes().size() -1).getLine() + 1
							));
				}
				arc.getEndNode().setLattitude(cords[cords.length-2]);
				arc.getEndNode().setLongtitude(cords[cords.length-1]);
				
				ArrayList<Coordinate> bC = new ArrayList<Coordinate>();
				for(int i=2; i<cords.length-2; i+=2){
					bC.add(new Coordinate(cords[i], cords[i+1]));
				}
				arc.setBreakCords(bC);
				arc.setBreakCount(bC.size());
				
				this.saveArcs(false,arc.getLine(),false);
			}
		}
		else if(object instanceof Arc){
			int pos = this.window.getArcList().findByLine(((Arc) object).getLine());
			
			if(pos>=0){
				this.window.getArcList().getArcs().set(pos, (Arc) object);
				
				this.saveArcs(false,((Arc) object).getLine(),false);
			}
		}
	}

	private void addArc(Object object) {
		if(object instanceof OMPoly){
			Arc arc = new Arc();
			
			float[] cords = ((OMPoly) object).getLatLonArray();
			if(this.window.getNodeList().findGraphicPosition(new OMPoint(cords[0], cords[1])) < 0){
				this.window.getNodeLayer().addNode(new Node(
						this.window.getNodeList().getNodes().get(this.window.getNodeList().getNodes().size() -1).getId() + 1,
						cords[1],
						cords[0],
						1,
						this.window.getNodeList().getNodes().get(this.window.getNodeList().getNodes().size() -1).getLine() + 1
						));
			}
			arc.setStartNode(new Node());
			arc.setEndNode(new Node());
			arc.getStartNode().setLattitude(cords[0]);
			arc.getStartNode().setLongtitude(cords[1]);
			if(this.window.getNodeList().findGraphicPosition(new OMPoint(cords[cords.length-2], cords[cords.length-1])) < 0){
				this.window.getNodeLayer().addNode(new Node(
						this.window.getNodeList().getNodes().get(this.window.getNodeList().getNodes().size() -1).getId() + 1,
						cords[cords.length-1],
						cords[cords.length-2],
						1,
						this.window.getNodeList().getNodes().get(this.window.getNodeList().getNodes().size() -1).getLine() + 1
						));
			}
			arc.getEndNode().setLattitude(cords[cords.length-2]);
			arc.getEndNode().setLongtitude(cords[cords.length-1]);
			
			ArrayList<Coordinate> bC = new ArrayList<Coordinate>();
			for(int i=2; i<cords.length-2; i+=2){
				bC.add(new Coordinate(cords[i], cords[i+1]));
			}
			arc.setBreakCords(bC);
			arc.setBreakCount(bC.size());
			
			arc.setLine(this.window.getArcList().getArcs().get(this.window.getArcList().getArcs().size()-1).getLine()+1);
			
			this.window.getArcList().getArcs().add(arc);
			
			this.saveArcs(true,arc.getLine(),false);
		}
	}

	private void delNode(Object object) {
		int pos = this.window.getNodeList().findGraphicPosition((OMPoint) object);
		if(pos >= 0){
			int line = this.window.getNodeList().getNodes().get(pos).getLine();
			this.window.getNodeList().changeLine(pos);
			
			this.window.getArcList().deleteIncludeNode(this.window.getNodeList().getNodes().get(pos).getId());
			
			this.window.getNodeList().getNodes().remove(pos);
			this.saveNodes(true,line,true);
		}
	}

	private void editNode(Object object) {
		if(object instanceof OMPoint){
			int pos = this.window.getNodeLayer().findGraphicPosition((OMPoint) object);
			if(pos >= 0){
				this.window.getNodeList().getNodes().get(pos).setLattitude(((OMPoint) object).getLat());
				this.window.getNodeList().getNodes().get(pos).setLongtitude(((OMPoint) object).getLon());
				
				this.saveNodes(false,this.window.getNodeList().getNodes().get(pos).getLine(),false);
			}
		}
		else if(object instanceof Node){
			Node pos = this.window.getNodeList().getNodeById(((Node)object).getId());
			pos.setLattitude(((Node)object).getLattitude());
			pos.setLongtitude(((Node)object).getLongtitude());
			
			this.saveNodes(false,((Node)object).getLine(),false);
		}
	}

	private void addNode(Object object) {
		if(object instanceof OMPoint){
			this.window.getNodeList().getNodes().add(new Node(
					this.window.getNodeList().getNodes().get(this.window.getNodeList().getNodes().size() -1).getId() + 1,
					((OMPoint)object).getLon(),
					((OMPoint)object).getLat(),
					0,
					this.window.getNodeList().getNodes().get(this.window.getNodeList().getNodes().size() -1).getLine() + 1
					));
			this.saveNodes(true,this.window.getNodeList().getNodes().get(this.window.getNodeList().getNodes().size() -1).getLine(),false);
		}
		else if(object instanceof Node){
			this.window.getNodeList().getNodes().add((Node)object);
			this.saveNodes(true,((Node)object).getLine(),false);
		}
	}
	
	/*
	 * header = false;
	 * line = -1;
	 * delete = false;
	 */
	private void saveArcs(boolean header, int line, boolean delete) {
		FileOutputStream fos;
		try {
			fos = new FileOutputStream(arcFile);
			PrintWriter out = new PrintWriter(fos);
			
			out.flush();
			out.println(fillTo(""+this.window.getArcList().getArcs().size()));
			
			for(int i=0; i<this.window.getArcList().getArcs().size(); i++)
				out.println(this.window.getArcList().getArcs().get(i).toString());
			
			out.close();
			
		} catch (IOException e) {
			System.out.println("blad zapisu: ARC - [header:"+header+"] [line:"+line+"] [delete:"+delete+"] \n\t [content:"+this.window.getArcList().getArcs().get(line-1).toString()+"]");
		}
	   
	}
	
	private void saveNodes(boolean header, int line, boolean delete){
		FileOutputStream fos;
		try {
			fos = new FileOutputStream(arcFile);
			PrintWriter out = new PrintWriter(fos);
			
			out.flush();
			out.println(fillTo(""+this.window.getNodeList().getNodes().size()));
			
			for(int i=0; i<this.window.getNodeList().getNodes().size(); i++)
				out.println(this.window.getNodeList().getNodes().get(i).toString());
			
			out.close();
			
		} catch (IOException e) {
			System.out.println("blad zapisu: NODE - [header:"+header+"] [line:"+line+"] [delete:"+delete+"] \n\t [content:"+this.window.getNodeList().getNodes().get(line-1).toString()+"]");
		}
	}
	
	private String fillTo(String size){
		String f = "";
		for(int i=0; i<10-size.length(); i++){
			f += " ";
		}
		f += size;
		return f;
	}
	
}
